/****************************************************************************
 *
 * Copyright 2016 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/****************************************************************************
 * fs/smartfs/smartfs_procfs.c
 *
 *   Copyright (C) 2013-2014 Ken Pettit. All rights reserved.
 *   Author: Ken Pettit <pettitkd@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <tinyara/config.h>

#include <sys/types.h>
#include <sys/statfs.h>
#include <sys/stat.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <tinyara/arch.h>
#include <tinyara/sched.h>
#include <tinyara/kmalloc.h>
#include <tinyara/fs/fs.h>
#include <tinyara/fs/procfs.h>
#include <tinyara/fs/dirent.h>
#include <tinyara/fs/ioctl.h>
#include <tinyara/fs/smart_procfs.h>

#include <arch/irq.h>
#include "smartfs.h"

#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_EXCLUDE_SMARTFS)

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/
/* This enumeration identifies all of the thread attributes that can be
 * accessed via the procfs file system.
 */

/* Level 1 is the directory of attributes */

struct smartfs_level1_s {
	struct procfs_dir_priv_s base;	/* Base directory private data */

	/* Add context specific data types here for managing the directory
	 * open / read / stat, etc.
	 */

	struct smartfs_mountpt_s *mount;
	uint8_t direntry;
};

/* This structure describes one open "file" */

struct smartfs_file_s {
	struct procfs_file_s base;	/* Base open file structure */

	/* Add context specific data types for managing an open file here */

	struct smartfs_level1_s level1;	/* Reference to item being accessed */
	uint16_t offset;
};

struct smartfs_procfs_entry_s {
	const char *name;			/* Name of the directory entry */
	size_t(*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
	ssize_t(*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
	uint8_t type;
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/
/* File system methods */

static int smartfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode);
static int smartfs_close(FAR struct file *filep);
static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
static ssize_t smartfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);

static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp);

static int smartfs_opendir(const char *relpath, FAR struct fs_dirent_s *dir);
static int smartfs_closedir(FAR struct fs_dirent_s *dir);
static int smartfs_readdir(FAR struct fs_dirent_s *dir);
static int smartfs_rewinddir(FAR struct fs_dirent_s *dir);

static int smartfs_stat(FAR const char *relpath, FAR struct stat *buf);

static ssize_t smartfs_debug_write(FAR struct file *filep, FAR const char *buffer, size_t buflen);
static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
static size_t smartfs_mem_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
#endif
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
static size_t smartfs_erasemap_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
#endif
#ifdef CONFIG_SMARTFS_FILE_SECTOR_DEBUG
static size_t smartfs_files_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
#endif

/****************************************************************************
 * Private Variables
 ****************************************************************************/

static const struct smartfs_procfs_entry_s g_direntry[] = {
	{"debuglevel", NULL, smartfs_debug_write, DTYPE_FILE},
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
	{"erasemap", smartfs_erasemap_read, NULL, DTYPE_FILE},
#endif
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
	{"mem", smartfs_mem_read, NULL, DTYPE_FILE},
#endif
	{"status", smartfs_status_read, NULL, DTYPE_FILE}
};

static const uint8_t g_direntrycount = sizeof(g_direntry) / sizeof(struct smartfs_procfs_entry_s);

/****************************************************************************
 * Public Variables
 ****************************************************************************/

/* See include/nutts/fs/procfs.h
 * We use the old-fashioned kind of initializers so that this will compile
 * with any compiler.
 */

const struct procfs_operations smartfs_procfsoperations = {
	smartfs_open,				/* open */
	smartfs_close,				/* close */
	smartfs_read,				/* read */

	/* No write supported */
	smartfs_write,				/* write */

	smartfs_dup,				/* dup */

	smartfs_opendir,			/* opendir */
	smartfs_closedir,			/* closedir */
	smartfs_readdir,			/* readdir */
	smartfs_rewinddir,			/* rewinddir */

	smartfs_stat				/* stat */
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: smartfs_find_dirref
 *
 * Description:
 *   Analyse relpath to find the directory reference entry it represents,
 *   if any.
 *
 ****************************************************************************/

static int smartfs_find_dirref(FAR const char *relpath, FAR struct smartfs_level1_s *level1)
{
	int ret = -ENOENT;
	FAR struct smartfs_mountpt_s *mount;
	uint16_t x;
	FAR char *str;

	mount = smartfs_get_first_mount();

	/* Skip the "fs/smartfs" portion of relpath */

	if (strncmp(relpath, "fs/smartfs", 10) == 0) {
		relpath += 10;
	}

	if (relpath[0] == '/') {
		relpath++;
	}

	/* Now test if doing a full dir listing of fs/smartfs */

	if (relpath[0] == '\0') {
		/* Save the mount as the first one to display */

		level1->mount = mount;
		level1->base.level = 1;
		level1->base.nentries = 0;
		while (mount != NULL) {
			level1->base.nentries++;
			mount = mount->fs_next;
		}

		level1->base.index = 0;
		ret = OK;
	} else {
		/* Search for the requested entry */

		str = strchr(relpath, '/');
		if (str) {
			x = str - relpath;
		} else {
			x = strlen(relpath);
		}

		while (mount) {
			if (strncmp(mount->fs_blkdriver->i_name, relpath, x) == 0) {
				/* Found the mount point.  Just break */

				break;
			}

			/* Try the next mount */

			mount = mount->fs_next;
		}

		if (mount) {
			/* Save the mount and skip it in the relpath */

			ret = OK;
			level1->mount = mount;
			relpath += strlen(mount->fs_blkdriver->i_name);
			if (relpath[0] == '/') {
				relpath++;
			}

			/* Test if a level 3 directory entry being requested or not */

			if (relpath[0] == '\0') {
				/* Requesting directory listing of a specific SMARTFS mount or entry */

				level1->base.level = 2;
				level1->base.nentries = g_direntrycount;
				level1->base.index = 0;
			} else {
				/* Find the level 3 directory entry */

				level1->base.level = 3;
				level1->base.nentries = 1;
				level1->base.index = 0;
				level1->direntry = 0;

				while (level1->direntry < g_direntrycount) {
					/* Test if this entry matches */

					if (strcmp(relpath, g_direntry[level1->direntry].name) == 0) {
						break;
					}

					/* Advance to next entry */

					level1->direntry++;
				}

				/* Test if entry found or not */

				if (level1->direntry == g_direntrycount) {
					ret = -ENOENT;
				}
			}
		}
	}

	return ret;
}

/****************************************************************************
 * Name: smartfs_open
 ****************************************************************************/

static int smartfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode)
{
	FAR struct smartfs_file_s *priv;
	int ret;

	fvdbg("Open '%s'\n", relpath);

	/* PROCFS is read-only.  Any attempt to open with any kind of write
	 * access is not permitted.
	 *
	 * REVISIT:  Write-able proc files could be quite useful.
	 */
	if (((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) && (!filep->f_inode->u.i_mops->write)) {
		fdbg("ERROR: Only O_RDONLY supported\n");
		return -EACCES;
	}

	/* Allocate a container to hold the task and attribute selection */

	priv = (FAR struct smartfs_file_s *)kmm_malloc(sizeof(struct smartfs_file_s));
	if (!priv) {
		fdbg("ERROR: Failed to allocate file attributes\n");
		return -ENOMEM;
	}

	/* Find the directory entry being opened */

	ret = smartfs_find_dirref(relpath, &priv->level1);
	if (ret == -ENOENT) {
		/* Entry not found */

		kmm_free(priv);
		return ret;
	}

	priv->offset = 0;

	/* Save the index as the open-specific state in filep->f_priv */

	filep->f_priv = (FAR void *)priv;
	return OK;
}

/****************************************************************************
 * Name: smartfs_close
 ****************************************************************************/

static int smartfs_close(FAR struct file *filep)
{
	FAR struct smartfs_file_s *priv;

	/* Recover our private data from the struct file instance */

	priv = (FAR struct smartfs_file_s *)filep->f_priv;
	DEBUGASSERT(priv);

	/* Release the file attributes structure */

	kmm_free(priv);
	filep->f_priv = NULL;
	return OK;
}

/****************************************************************************
 * Name: smartfs_read
 ****************************************************************************/

static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
	FAR struct smartfs_file_s *priv;
	ssize_t ret;

	fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);

	/* Recover our private data from the struct file instance */

	priv = (FAR struct smartfs_file_s *)filep->f_priv;
	DEBUGASSERT(priv);

	/* Perform the read based on the directory entry */

	ret = 0;

	if (priv->level1.base.level == 3) {
		if (priv->level1.direntry < g_direntrycount) {
			if (g_direntry[priv->level1.direntry].read) {
				ret = g_direntry[priv->level1.direntry].read(filep, buffer, buflen);
			}
		}
	}

	/* Update the file offset */

	if (ret > 0) {
		filep->f_pos += ret;
	}

	return ret;
}

/****************************************************************************
 * Name: smartfs_write
 ****************************************************************************/

static ssize_t smartfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
{
	FAR struct smartfs_file_s *priv;
	ssize_t ret;

	/* Recover our private data from the struct file instance */

	priv = (FAR struct smartfs_file_s *)filep->f_priv;
	DEBUGASSERT(priv);

	/* Perform the write based on the directory entry */

	ret = 0;

	if (priv->level1.base.level == 3) {
		if (priv->level1.direntry < g_direntrycount) {
			if (g_direntry[priv->level1.direntry].write) {
				ret = g_direntry[priv->level1.direntry].write(filep, buffer, buflen);
			}
		}
	}

	/* Update the file offset */

	if (ret > 0) {
		filep->f_pos += ret;
	}

	return ret;
}

/****************************************************************************
 * Name: smartfs_dup
 *
 * Description:
 *   Duplicate open file data in the new file structure.
 *
 ****************************************************************************/

static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
	FAR struct smartfs_file_s *oldpriv;
	FAR struct smartfs_file_s *newpriv;

	fvdbg("Dup %p->%p\n", oldp, newp);

	/* Recover our private data from the old struct file instance */

	oldpriv = (FAR struct smartfs_file_s *)oldp->f_priv;
	DEBUGASSERT(oldpriv);

	/* Allocate a new container to hold the task and attribute selection */

	newpriv = (FAR struct smartfs_file_s *)kmm_malloc(sizeof(struct smartfs_file_s));
	if (!newpriv) {
		fdbg("ERROR: Failed to allocate file attributes\n");
		return -ENOMEM;
	}

	/* The copy the file attributes from the old attributes to the new */

	memcpy(newpriv, oldpriv, sizeof(struct smartfs_file_s));

	/* Save the new attributes in the new file structure */

	newp->f_priv = (FAR void *)newpriv;
	return OK;
}

/****************************************************************************
 * Name: smartfs_opendir
 *
 * Description:
 *   Open a directory for read access
 *
 ****************************************************************************/

static int smartfs_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir)
{
	FAR struct smartfs_level1_s *level1;
	int ret;

	fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
	DEBUGASSERT(relpath && dir && !dir->u.procfs);

	/* The path refers to the 1st level subdirectory.  Allocate the level1
	 * dirent structure.
	 */

	level1 = (FAR struct smartfs_level1_s *)
			 kmm_malloc(sizeof(struct smartfs_level1_s));

	if (!level1) {
		fdbg("ERROR: Failed to allocate the level1 directory structure\n");
		return -ENOMEM;
	}

	/* Initialize base structure components */

	ret = smartfs_find_dirref(relpath, level1);

	if (ret == OK) {
		dir->u.procfs = (FAR void *)level1;
	} else {
		kmm_free(level1);
	}

	return ret;
}

/****************************************************************************
 * Name: smartfs_closedir
 *
 * Description: Close the directory listing
 *
 ****************************************************************************/

static int smartfs_closedir(FAR struct fs_dirent_s *dir)
{
	FAR struct smartfs_level1_s *priv;

	DEBUGASSERT(dir && dir->u.procfs);
	priv = dir->u.procfs;

	if (priv) {
		kmm_free(priv);
	}

	dir->u.procfs = NULL;
	return OK;
}

/****************************************************************************
 * Name: smartfs_readdir
 *
 * Description: Read the next directory entry
 *
 ****************************************************************************/

static int smartfs_readdir(struct fs_dirent_s *dir)
{
	FAR struct smartfs_level1_s *level1;
	int ret, index;

	DEBUGASSERT(dir && dir->u.procfs);
	level1 = dir->u.procfs;

	/* Have we reached the end of the directory */

	index = level1->base.index;
	if (index >= level1->base.nentries) {
		/* We signal the end of the directory by returning the special
		 * error -ENOENT
		 */

		fvdbg("Entry %d: End of directory\n", index);
		ret = -ENOENT;
	}

	/* We are traversing a subdirectory of task attributes */

	else {
		DEBUGASSERT(level1->base.level >= 1);

		/* Test the type of directory listing */

		if (level1->base.level == 1) {
			/* Listing the top level (mounted smartfs volumes) */

			if (!level1->mount) {
				return -ENOENT;
			}

			dir->fd_dir.d_type = DTYPE_DIRECTORY;
			strncpy(dir->fd_dir.d_name, level1->mount->fs_blkdriver->i_name, NAME_MAX + 1);

			/* Advance to next entry */

			level1->base.index++;
			level1->mount = level1->mount->fs_next;
		} else if (level1->base.level == 2) {
			/* Listing the contents of a specific mount */

			dir->fd_dir.d_type = g_direntry[level1->base.index].type;
			strncpy(dir->fd_dir.d_name, g_direntry[level1->base.index++].name, NAME_MAX + 1);
		} else if (level1->base.level == 3) {
			/* Listing the contents of a specific entry */

			dir->fd_dir.d_type = g_direntry[level1->base.index].type;
			strncpy(dir->fd_dir.d_name, g_direntry[level1->direntry].name, NAME_MAX + 1);
			level1->base.index++;
		}

		/* Set up the next directory entry offset.  NOTE that we could use the
		 * standard f_pos instead of our own private index.
		 */

		ret = OK;
	}

	return ret;
}

/****************************************************************************
 * Name: smartfs_rewindir
 *
 * Description: Reset directory read to the first entry
 *
 ****************************************************************************/

static int smartfs_rewinddir(struct fs_dirent_s *dir)
{
	FAR struct smartfs_level1_s *priv;

	DEBUGASSERT(dir && dir->u.procfs);
	priv = dir->u.procfs;

	priv->base.index = 0;
	return OK;
}

/****************************************************************************
 * Name: smartfs_stat
 *
 * Description: Return information about a file or directory
 *
 ****************************************************************************/

static int smartfs_stat(const char *relpath, struct stat *buf)
{
	int ret;
	struct smartfs_level1_s level1;

	/* Decide if the relpath is valid and if it is a file
	 *        or a directory and set it's permissions.
	 */

	ret = smartfs_find_dirref(relpath, &level1);

	buf->st_mode = S_IROTH | S_IRGRP | S_IRUSR;
	if (ret == OK) {
		if (level1.base.level < 3) {
			buf->st_mode |= S_IFDIR;
		} else {
			/* The entry being stat'ed is lowest level */

			if (g_direntry[level1.direntry].type == DTYPE_DIRECTORY) {
				buf->st_mode |= S_IFDIR;
			} else {
				buf->st_mode |= S_IFREG;
			}

			/* Test if the entry is writable */

			if (g_direntry[level1.direntry].write != NULL) {
				buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR;
			}
		}
	}

	/* File/directory size, access block size */

	buf->st_size = 0;
	buf->st_blksize = 0;
	buf->st_blocks = 0;

	return ret;
}

/****************************************************************************
 * Name: smartfs_debug_write
 *
 * Description: Performs the write operation for the "debug" file
 *
 ****************************************************************************/

static ssize_t smartfs_debug_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
{
	struct mtd_smart_debug_data_s debug_data;
	FAR struct smartfs_file_s *priv;

	priv = (FAR struct smartfs_file_s *)filep->f_priv;

	/* Populate the debug_data structure */

	debug_data.debugcmd = SMART_DEBUG_CMD_SET_DEBUG_LEVEL;
	debug_data.debugdata = atoi(buffer);

	priv->level1.mount->fs_blkdriver->u.i_bops->ioctl(priv->level1.mount->fs_blkdriver, BIOC_DEBUGCMD, (unsigned long)&debug_data);

	return buflen;
}

/****************************************************************************
 * Name: smartfs_status_read
 *
 * Description: Performs the read operation for the "status" dir entry.
 *
 ****************************************************************************/

static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
	struct mtd_smart_procfs_data_s procfs_data;
	FAR struct smartfs_file_s *priv;
	int ret;
	size_t len;
#ifdef CONFIG_DEBUG_FS
	int utilization;
#endif
	priv = (FAR struct smartfs_file_s *)filep->f_priv;

	/* Initialize the read length to zero and test if we are at the
	 * end of the file (i.e. already read the data.
	 */

	len = 0;
	if (priv->offset == 0) {
		/* Get the ProcFS data from the block driver */

		ret = priv->level1.mount->fs_blkdriver->u.i_bops->ioctl(priv->level1.mount->fs_blkdriver, BIOC_GETPROCFSD, (unsigned long)&procfs_data);

		if (ret == OK) {
			/* Format and return data in the buffer */
			len = snprintf(buffer, buflen, "Total Sectors    %d\nFree Sectors     %d\n" "Released Sectors %d\n", procfs_data.totalsectors, procfs_data.freesectors, procfs_data.releasesectors);
#ifdef CONFIG_DEBUG_FS
			/* Calculate the sector utilization percentage */
			if (procfs_data.blockerases == 0) {
				utilization = 100;
			} else {
				utilization = 100 * (procfs_data.blockerases * procfs_data.sectorsperblk - procfs_data.unusedsectors) / (procfs_data.blockerases * procfs_data.sectorsperblk);
			}

			fdbg("Format version   %d\nName Len         %d\n" "Sector Size      %d\nFormat Sector    %d\n" "Dir Sector       %d\nUnused Sectors   %d\n" "Block Erases     %d\nSectors/Block    %d\n" "Utilization      %d%%\n"
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
				 "Uneven Wear Count %d\n"
#endif
				 , procfs_data.formatversion, procfs_data.namelen, procfs_data.sectorsize, procfs_data.formatsector, procfs_data.dirsector, procfs_data.unusedsectors, procfs_data.blockerases, procfs_data.sectorsperblk, utilization
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
				 , procfs_data.uneven_wearcount
#endif
				);

#endif							/* end of CONFIG_DEBUG_FS */
		}

		/* Indicate we have already provided all the data */

		priv->offset = 0xFF;
	}

	return len;
}

/****************************************************************************
 * Name: smartfs_mem_read
 *
 * Description: Performs the read operation for the "mem" dir entry.
 *
 ****************************************************************************/

#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
static size_t smartfs_mem_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
	struct mtd_smart_procfs_data_s procfs_data;
	FAR struct smartfs_file_s *priv;
	int ret;
	uint16_t x;
	size_t len, total;

	priv = (FAR struct smartfs_file_s *)filep->f_priv;

	/* Initialize the read length to zero and test if we are at the
	 * end of the file (i.e. already read the data.
	 */

	len = 0;
	if (priv->offset == 0) {
		/* Get the ProcFS data from the block driver */

		ret = priv->level1.mount->fs_blkdriver->u.i_bops->ioctl(priv->level1.mount->fs_blkdriver, BIOC_GETPROCFSD, (unsigned long)&procfs_data);

		if (ret == OK) {
			/* Print the allocations to the buffer */

			total = 0;
			len = snprintf(buffer, buflen, "Allocations:\n");
			buflen -= len;
			for (x = 0; x < procfs_data.alloccount; x++) {
				/* Only print allocations with a non-NULL pointer */

				if (procfs_data.allocs[x].ptr != NULL) {
					len += snprintf(&buffer[len], buflen - len, "   %s: %d\n", procfs_data.allocs[x].name, procfs_data.allocs[x].size);
					total += procfs_data.allocs[x].size;
				}
			}

			/* Add the total allocation amount to the buffer */

			len += snprintf(&buffer[len], buflen - len, "\nTotal: %d\n", total);
		}

		/* Indicate we have done the read */

		priv->offset = 0xFF;
	}

	return len;
}
#endif

/****************************************************************************
 * Name: smartfs_erasemap_read
 *
 * Description: Performs the read operation for the "erase" dir entry.
 *
 ****************************************************************************/

#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
static size_t smartfs_erasemap_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
{
	struct mtd_smart_procfs_data_s procfs_data;
	FAR struct smartfs_file_s *priv;
	int ret, rows, cols;
	size_t x, y;
	size_t len, copylen;

	priv = (FAR struct smartfs_file_s *)filep->f_priv;

	/* Get the ProcFS data from the block driver */

	ret = priv->level1.mount->fs_blkdriver->u.i_bops->ioctl(priv->level1.mount->fs_blkdriver, BIOC_GETPROCFSD, (unsigned long)&procfs_data);
	if (ret != OK) {
		return 0;
	}

	/* Initialize the read length to zero and test if we are at the
	 * end of the file (i.e. already read the data).
	 */

	len = 0;
	rows = 32;
	cols = procfs_data.neraseblocks / rows;
	while (rows >= 4 && (cols < 64 || cols > 128)) {
		rows >>= 1;
		cols = procfs_data.neraseblocks / rows;
	}

	/* Continue sending data until everything sent.  We add 'rows' below to
	 * account for the \n at the end of each line.
	 */

	if (priv->offset < procfs_data.neraseblocks + rows) {
		/* copylen keeps track of the current length.  When it is
		 * equal to or greater than the offset, we start sending data
		 * again.  Basically we are starting at the beginning each time
		 * and only sending where we left off and discarding the rest.
		 */

		copylen = 0;
		for (y = 0; y < rows; y++) {
			//for (x = 0; x < 128; x++)
			for (x = 0; x < cols; x++) {
				/* Copy data to the buffer */

				if (copylen >= priv->offset) {
					buffer[len++] = procfs_data.erasecounts[y * cols + x] + 'A';
					priv->offset++;

					if (len >= buflen) {
						return len;
					}
				}
				copylen++;
			}

			/* Add a trailing \n */

			if (copylen >= priv->offset) {
				buffer[len++] = '\n';
				priv->offset++;
				if (len >= buflen) {
					return len;
				}
			}

			/* Terminate the string */

			if (copylen >= priv->offset) {
				buffer[len++] = '\0';
				priv->offset++;
			}

			copylen++;
		}
	}

	return len;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

#endif							/* CONFIG_FS_PROCFS && !CONFIG_FS_PROCFS_EXCLUDE_SMARTFS */
